﻿using System;
using System.Net;
using System.Net.Sockets;

namespace Traceroute
{
	internal class Program
	{
		private static void Main(string[] args)
		{
			if (args.Length < 1)
			{
				Console.WriteLine("Usage: traceroute <host> [<count> [<timeout in ms>]]");
				return;
			}
			String address = args[0];
			short jumps = 30;
			if (args.Length > 1)
			{
				try
				{
					jumps = Convert.ToInt16(args[1]);
				}
				catch
				{
					Console.WriteLine("{0} is invalid jump count, using default {1}", args[1], jumps);
				}
			}

			short timeout = 1000;
			if (args.Length > 2)
			{
				try
				{
					timeout = Convert.ToInt16(args[2]);
				}
				catch
				{
					Console.WriteLine("{0} is invalid timeout value, using default {1}", args[2], timeout);
				}
			}

			var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
			socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, false);
			socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout);
			EndPoint myPoint = new IPEndPoint(IPAddress.Any, 0);
			socket.Bind(myPoint);
			EndPoint destination;
			IPAddress destAddress;
			try
			{
				destAddress = Dns.GetHostAddresses(address)[0];
			}
			catch (SocketException e)
			{
				Console.WriteLine("Unable to resolve ip-address of {0}", address);
				return;
			}
			Console.WriteLine("Traceroute to {0}", destAddress);
			destination = new IPEndPoint(destAddress, 0);
			var header = new IcmpHeader();
			header.Type = 8; //echo request
			var message = new byte[] {1, 2, 3, 4};
			byte[] packet = header.PackData(message);

			var receiveBuffer = new Byte[256];
			DateTime start;
			EndPoint remote;
			for (short i = 1; i <= jumps; ++i)
			{
				socket.Ttl = i;
				start = DateTime.Now;
				socket.SendTo(packet, destination);
				try
				{
					remote = new IPEndPoint(Dns.GetHostAddresses(address)[0], 0);
					socket.ReceiveFrom(receiveBuffer, ref remote);
				}
				catch
				{
					Console.WriteLine("{0}: Timeout", i);
					continue;
				}
				TimeSpan interval = DateTime.Now - start;
				if (receiveBuffer[20] == 11)
				{
					Console.WriteLine("{0}: {1}ms {2}", i, interval.Milliseconds, remote);
				}

				if (receiveBuffer[20] == 0) //echo reply
				{
					Console.WriteLine("{0}: {1}ms {2} - Target reached", i, interval.Milliseconds, remote);
					break;
				}
			}
			Console.WriteLine("Traceroute finished");
		}
	}
}